home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
TPUG - Toronto PET Users Group
/
TPUG Users Group CD
/
TPUG Users Group CD.iso
/
AMIGA
/
(A)G
/
(A)G11.ADF
/
Maze
/
maze.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-08-17
|
37KB
|
1,126 lines
#include <exec/types.h>
#include <intuition/intuition.h>
#include <functions.h>
#include <graphics/gfxmacros.h>
#include <graphics/text.h>
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct RastPort tmpRPactual;
struct RastPort *tmpRP = &tmpRPactual;
struct BitMap tmpBM;
struct TextAttr font = {
(STRPTR)"topaz.font", /* Font Name */
TOPAZ_EIGHTY, /* Font Height */
FS_NORMAL, /* Font Style */
FPF_ROMFONT /* Preferences */
};
#define tmpBMxy 60L
#define windowW 552L
#define windowH 183L
struct NewWindow nw = {
6, 12, windowW, windowH, /* EDGES, SIZE */
0, 1, /* PENS */
CLOSEWINDOW, /* IDCMPFlags */
WINDOWDRAG | WINDOWDEPTH | /* Flags (requested window features)*/
WINDOWCLOSE | ACTIVATE | REPORTMOUSE,
NULL, /* gadgets */
NULL, /* checkmark image */
NULL, /* title */
NULL, /* screen */
NULL, /* bitmap */
0,0,0,0, /* min and max w and h */
WBENCHSCREEN, /* TYPE */
};
struct IntuiText it_GiveUp = {
0,1, /* frontpen, backpen */
JAM1, /* drawmode */
1,1, /* leftedge, topedge */
&font, /* TextAttr * ITextFont */
(UBYTE *)" Give Up! ", /* IText */
NULL, /* NextText */
};
struct MenuItem mi_GiveUp = {
(struct MenuItem *) NULL,
0,40, /* LeftEdge, TopEdge */
21*9,10, /* Width, Height */
ITEMTEXT | HIGHCOMP | ITEMENABLED, /* Flags */
0, /* Mutual Exclude */
(APTR)&it_GiveUp, /* ItemFill */
NULL, /* SelectFill */
0, /* Command */
NULL, /* Subitem */
0, /* NextSelect */
};
struct IntuiText it_3Level = {
0,1, /* frontpen, backpen */
JAM1, /* drawmode */
1,1, /* leftedge, topedge */
&font, /* TextAttr * ITextFont */
(UBYTE *)"New 3-Level Maze", /* IText */
NULL, /* NextText */
};
struct MenuItem mi_3Level = {
(struct MenuItem *) &mi_GiveUp,
0,30, /* LeftEdge, TopEdge */
21*9,10, /* Width, Height */
ITEMTEXT | HIGHCOMP | ITEMENABLED, /* Flags */
0, /* Mutual Exclude */
(APTR)&it_3Level, /* ItemFill */
NULL, /* SelectFill */
0, /* Command */
NULL, /* Subitem */
0, /* NextSelect */
};
struct IntuiText it_2Level = {
0,1, /* frontpen, backpen */
JAM1, /* drawmode */
1,1, /* leftedge, topedge */
&font, /* TextAttr * ITextFont */
(UBYTE *)"New 2-Level Maze", /* IText */
NULL, /* NextText */
};
struct MenuItem mi_2Level = {
(struct MenuItem *) &mi_3Level,
0,20, /* LeftEdge, TopEdge */
21*9,10, /* Width, Height */
ITEMTEXT | HIGHCOMP | ITEMENABLED, /* Flags */
0, /* Mutual Exclude */
(APTR)&it_2Level, /* ItemFill */
NULL, /* SelectFill */
0, /* Command */
NULL, /* Subitem */
0, /* NextSelect */
};
struct IntuiText it_1Level = {
0,1, /* frontpen, backpen */
JAM1, /* drawmode */
1,1, /* leftedge, topedge */
&font, /* TextAttr * ITextFont */
(UBYTE *)"New 1-Level Maze", /* IText */
NULL, /* NextText */
};
struct MenuItem mi_1Level = {
(struct MenuItem *)&mi_2Level,
0,10, /* LeftEdge, TopEdge */
21*9,10, /* Width, Height */
ITEMTEXT | HIGHCOMP | ITEMENABLED, /* Flags */
0, /* Mutual Exclude */
(APTR)&it_1Level, /* ItemFill */
NULL, /* SelectFill */
0, /* Command */
NULL, /* Subitem */
0, /* NextSelect */
};
struct IntuiText it_About = {
0,1, /* frontpen, backpen */
JAM1, /* drawmode */
1,1, /* leftedge, topedge */
&font, /* TextAttr * ITextFont */
(UBYTE *)"About TML's AmigaMaze", /* IText */
NULL, /* NextText */
};
struct MenuItem mi_About = {
(struct MenuItem *) &mi_1Level,
0,0, /* LeftEdge, TopEdge */
21*9,10, /* Width, Height */
ITEMTEXT | HIGHCOMP | ITEMENABLED, /* Flags */
0, /* Mutual Exclude */
(APTR)&it_About, /* ItemFill */
NULL, /* SelectFill */
0, /* Command */
NULL, /* Subitem */
0, /* NextSelect */
};
struct Menu menu = {
(struct Menu *) NULL, /* NEXT menu */
0, 0, 12*9, 0, /* LeftEdge, TopEdge, Width, Height */
MENUENABLED, /* Flags */
(BYTE *) "Maze Control", /* MenuName */
(struct MenuItem *)&mi_About, /* First item */
0,0,0,0, /* JazzX,JazzY, BeatX, BeatY */
};
struct Window *window;
/* BEWARE: NOWHERE has a dual nature, being used as a doesn't-go-anywhere
indicator, and also as an invalid level marker!!! */
#define NOWHERE -1
#define SOUTH 0
#define EAST 1
#define WEST 2
#define NORTH 3
#define SOLVED -3
#define DIRECTIONS 4
#define MAXLEVELS 3
#define BOARDMAXX 36
#define BOARDMAXY 29
/*** the following are "inpath" stata. (see struct square) *******/
#define NOTTRAVERSED 1
#define EDGE 2
#define LASTPIECE 3 + 32
#define FIRSTPIECE 3 + 64
#define CURPIECE 3 + 96
#define TRAVERSED 3
#define MIDCMP1 CLOSEWINDOW|MOUSEMOVE|MOUSEBUTTONS|MENUPICK|MENUVERIFY
#define MIDCMP2 REQCLEAR
#define MIDCMP3 CLOSEWINDOW
extern ULONG RangeRand();
extern int about();
extern int cycle();
typedef struct {
int conlev[ DIRECTIONS ]; /** level connected to in each dir. **/
int inpath; /** Also the color this should be drawn in. **/
int compass; /** Which direction to go to get home from here **/
} square;
typedef square BOARD[ BOARDMAXX+1 ][ BOARDMAXY+1 ][ MAXLEVELS ];
BOARD board;
struct RastPort *rp;
int StartX, StartY, StartLevel;
int EndX, EndY, EndLevel;
int CurX, CurY, CurLevel;
int NewX, NewY, NewLevel;
int MouseX, MouseY;
int MinLevel, MaxLevel;
int BoardMaxX = BOARDMAXX;
int BoardMaxY = BOARDMAXY;
int SquareXsize = 34;
int SquareYsize = 18;
/* We need to have some Fill patterns for various places. Here goes... */
USHORT Pat_Normal[] = { /** the normal filled pattern **/
0xffff, /* plane 1 pattern */
0xffff,
0xffff, /* plane 2 pattern */
0xffff };
USHORT Pat_P1[] = { /** first dithered filled pattern **/
0x5555, /* plane 1 pattern */
0xaaaa,
0xffff, /* plane 2 pattern */
0xffff };
USHORT Pat_P2[] = { /** second dithered filled pattern **/
0xffff, /* plane 1 pattern */
0xffff,
0x5555, /* plane 2 pattern */
0xaaaa };
setHint(ShowHint)
int ShowHint;
{
char *s;
int oldBPen;
if (ShowHint) {
switch ( board[CurX][CurY][CurLevel].compass) {
case NORTH : s = " HINT: Go North! "; break;
case SOUTH : s = " HINT: Go South! "; break;
case EAST : s = " HINT: Go East! "; break;
case WEST : s = " HINT: Go West! "; break;
case SOLVED: s = " CONGRATULATIONS! "; break;
default : s = " Hmm. I don't know. "; break;
}
}
else {
s = " (Press Left Button for a hint.)";
}
Move(rp, 20L, 180L);
SetAPen(rp, 1L);
oldBPen = rp -> BgPen;
SetBPen(rp, 0L);
SetDrMd(rp, (LONG)JAM2);
Text(rp, s, 32L);
SetBPen(rp, (LONG)oldBPen);
}
showside(ax,ay, bx,by, cx,cy, dx,dy, color)
LONG ax,ay, bx,by, cx,cy, dx,dy, color;
{
SetAPen( tmpRP, color); /* The shape isn't allways a rect., so */
SetOPen( tmpRP, color); /* we can't use RectFill() here... */
BNDRYOFF( tmpRP); /* Boundaries look bad w/ patterns...*/
switch (color) {
case CURPIECE :
case TRAVERSED : SetAfPt( tmpRP, Pat_P2,-1L); break;
case LASTPIECE : SetAfPt( tmpRP, Pat_P1,-1L); break;
case NOTTRAVERSED :
case FIRSTPIECE :
default : SetAfPt( tmpRP, Pat_Normal,-1L); break;
}
AreaMove( tmpRP, ax,ay);
AreaDraw( tmpRP, bx,by);
AreaDraw( tmpRP, cx,cy);
AreaDraw( tmpRP, dx,dy);
AreaEnd( tmpRP);
SetAPen( tmpRP, (LONG)EDGE);
Move( tmpRP, ax, ay);
Draw( tmpRP, bx, by);
Draw( tmpRP, bx+1,by);
Draw( tmpRP, ax+1,ay);
Move( tmpRP, cx, cy);
Draw( tmpRP, dx, dy);
Draw( tmpRP, dx+1,dy);
Draw( tmpRP, cx+1,cy);
}
putedge(ax,ay,bx,by)
LONG ax,ay,bx,by;
{
SetAPen(tmpRP,(LONG)EDGE);
Move(tmpRP,ax, ay);
Draw(tmpRP,bx, by);
Draw(tmpRP,bx+1,by);
Draw(tmpRP,ax+1,ay);
}
LONG renderColor(x, y, z, x1, y1, z1)
int x, y, z, x1, y1, z1;
{ int color;
color = board[x][y][z].inpath;
if (!(z == MaxLevel-1 && (color == FIRSTPIECE || color == LASTPIECE)))
if ((color == NOTTRAVERSED) ||
(board[x1][y1][z1].inpath == NOTTRAVERSED))
color = NOTTRAVERSED;
return((LONG)color);
}
#define XDIF 6
#define YDIF 3
render(x,y)
int x, y;
{
LONG ax,ay, bx,by, cx,cy, dx,dy, color;
int level, Bleft, Btop, deltaX, deltaY, tmpL;
int left, right, bottom, top;
/* clear the temporary bitmap */
SetAPen( tmpRP, 0L);
SetBPen( tmpRP, 0L);
RectFill( tmpRP, 0L, 0L, (LONG)SquareXsize, (LONG)SquareYsize );
Bleft = 4 + (x - 1) * SquareXsize;
Btop = 11 + (y - 1) * SquareYsize;
left = 0;
right = SquareXsize - 1;
top = 0;
bottom = SquareYsize - 1;
for (level=MinLevel; level<MaxLevel; level++) {
if (references(x, y, level) == 0)
continue; /* This level doesn't go anywhere. */
deltaY = YDIF * level + 1;
deltaX = XDIF * level + 1;
/**** Fill in the middle part first ****/
ax = right - deltaX; ay = top + deltaY;
bx = left + deltaX; by = ay;
cx = bx; cy = bottom - deltaY;
dx = ax; dy = cy;
color = board[x][y][level].inpath;
showside(ax,ay,bx,by,cx,cy,dx,dy,color);
/**** Do the NORTH side of this level first ****/
ax = right-deltaX; ay = top + deltaY;
bx = ax; by = top;
cx = left +deltaX; cy = by;
dx = cx; dy = ay;
tmpL = board[x][y][level].conlev[NORTH];
if ( tmpL == NOWHERE) {
putedge(ax,ay,dx,dy);
goto DrawWest;
}
color = renderColor(x,y,level,x,y-1,tmpL);
if (tmpL != level && level == 1) {
bx = bx - XDIF*(tmpL - 1);
cx = cx + XDIF*(tmpL - 1);
}
showside(ax,ay,bx,by,cx,cy,dx,dy,color);
/**** Do the WEST side of this level next ****/
DrawWest:
ax = left + deltaX; ay = top + deltaY;
bx = left; by = ay;
cx = bx; cy = bottom - deltaY;
dx = ax; dy = cy;
tmpL = board[x][y][level].conlev[WEST];
if ( tmpL == NOWHERE) {
putedge(ax,ay,dx,dy);
goto DrawSouth;
}
color = renderColor(x,y,level,x-1,y,tmpL);
if (tmpL != level && level == 1) {
by = by + YDIF*(tmpL - 1);
cy = cy - YDIF*(tmpL - 1);
}
showside(ax,ay,bx,by,cx,cy,dx,dy,color);
/**** Do the SOUTH side of this level next ****/
DrawSouth:
ax = left + deltaX; ay = bottom - deltaY;
bx = ax; by = bottom;
cx = right - deltaX; cy = by;
dx = cx; dy = ay;
tmpL = board[x][y][level].conlev[SOUTH];
if ( tmpL == NOWHERE) {
putedge(ax,ay,dx,dy);
goto DrawEast;
}
color = renderColor(x,y,level,x,y+1,tmpL);
if (tmpL != level && level == 1) {
bx = bx + XDIF*(tmpL-1);
cx = cx - XDIF*(tmpL-1);
}
showside(ax,ay,bx,by,cx,cy,dx,dy,color);
/**** Do the EAST side of this level last ****/
DrawEast:
ax = right - deltaX; ay = bottom - deltaY;
bx = right; by = ay;
cx = bx; cy = top + deltaY;
dx = ax; dy = cy;
tmpL = board[x][y][level].conlev[EAST];
if ( tmpL == NOWHERE) {
putedge(ax,ay,dx,dy);
goto DrawComplete;
}
color = renderColor(x,y,level,x+1,y,tmpL);
if (tmpL != level && level == 1) {
by = by - YDIF*(tmpL - 1);
cy = cy + YDIF*(tmpL - 1);
}
showside(ax,ay,bx,by,cx,cy,dx,dy,color);
DrawComplete:;
}
/* now copy temporary bitmap into window */
BltBitMapRastPort(&tmpBM,0L,0L, rp,(LONG)Bleft,(LONG)Btop,
(LONG)SquareXsize,(LONG)SquareYsize, (LONG)0x0c0);
} /* end of render() */
/* references() returns the number connections from a given square. */
int references(x, y, level)
int x, y, level;
{ int i, refs;
refs = 0;
for (i=0; i < DIRECTIONS; i++)
if (board[x][y][level].conlev[i] != NOWHERE)
refs++;
return(refs);
}
/* linkable() returns true if we can connect the two indicated squares. */
/* NOTE: We don't allow connecting to the second square if it */
/* is already part of the maze. This test if the first if stmt.*/
/* The second test if more subtle. The problem being tested for is shown in */
/* the following diagram. This is an edge-on view of the board: */
/* a __ __ d */
/* \ / */
/* / */
/* / */
/* c __/ \__ b */
/* If c and d are connected, I can't connect a to b because the paths would */
/* intersect. The way the test is written, it will work regardless of whether */
/* the new path is going up, down, or level. */
int linkable(x1,y1,level1,dir1, x2,y2, level2, dir2)
int x1,y1,level1,dir1, x2,y2,level2;
{
int tmp;
if (references(x2,y2,level2)) return(0);
if (board[x1][y1][level2].conlev[dir1] == level1) return(0);
return(-1);
}
/* Returns the other Direction, or NOWHERE if an invalid direction is passed. */
int otherDir(nd)
int nd;
{ switch (nd) {
case NORTH : return(SOUTH); break;
case SOUTH : return(NORTH); break;
case EAST : return(WEST) ; break;
case WEST : return(EAST) ; break;
}
return(NOWHERE);
}
/* makepathlist() returns the number of directions we can go from here w/o */
/* backtracking -- i.e., not counting the way we got here. These */
/* "uncharted" routes are characterized by .compus==NOWHERE. The list of */
/* directions is placed in pl[]. */
int makepathlist(x,y,z,pl)
int x, y, z, pl[];
{ int di, le, paths;
paths = 0;
for (di=0; di<DIRECTIONS; di++) {
pl[paths] = di;
le = board[x][y][z].conlev[di];
if (le != NOWHERE)
switch (di) {
case NORTH : if (board[x][y-1][le].compass == NOWHERE)
paths++;
break;
case SOUTH : if (board[x][y+1][le].compass == NOWHERE)
paths++;
break;
case EAST : if (board[x+1][y][le].compass == NOWHERE)
paths++;
break;
case WEST : if (board[x-1][y][le].compass == NOWHERE)
paths++;
break;
case NOWHERE : break;
}
}
return(paths);
}
int topscore;
/* buildreturnmap() is a recursive routine which starts the end of a path on */
/* our tree-structured maze and traverses up to an intersection or dead end. */
/* When we hit a dead end, we just return, but when we hit an intersection, we */
/* call buildreturnmap() again, once for each path going off from the */
/* intersection, except for the one got there on. We keep a score for how far */
/* we are from the first square. One point per square, plus 5 times the */
/* number of paths leading off from each intersection. We keep a topscore to */
/* find out which square is farthest (in this modified sense) from our */
/* starting square. This helps to ensure that we get a reasonably difficult */
/* maze to solve. */
/* We also record the direction to the end of the maze in each square. This */
/* information is used by the hint facility. */
buildreturnmap(x,y,z,score)
int x, y, z, score;
{ int pathlist[DIRECTIONS], paths, i, le;
while ( (paths = makepathlist(x,y,z,pathlist)) == 1) {
z = board[x][y][z].conlev[pathlist[0]];
switch (pathlist[0]) {
case NORTH : y--; break;
case SOUTH : y++; break;
case EAST : x++; break;
case WEST : x--; break;
}
board[x][y][z].compass = otherDir(pathlist[0]);
/* score++; */
}
if (paths == 0) { /* We are at the end of a path... */
if (score >= topscore) {
topscore = score;
StartX = x;
StartY = y;
StartLevel = z;
}
return;
}
/* if we got here, we are not at the end of a path. paths > 1 */
score = score + paths;
for (i=0; i<paths; i++) {
le = board[x][y][z].conlev[pathlist[i]];
switch (pathlist[i]) {
case NORTH : board[x][y-1][le].compass = SOUTH;
buildreturnmap(x,y-1,le,score); break;
case SOUTH : board[x][y+1][le].compass = NORTH;
buildreturnmap(x,y+1,le,score); break;
case EAST : board[x+1][y][le].compass = WEST;
buildreturnmap(x+1,y,le,score); break;
case WEST : board[x-1][y][le].compass = EAST;
buildreturnmap(x-1,y,le,score); break;
}
}
}
#define MAXENDS 120
#define FREEEND -1
#define LASTEND -1
typedef struct {
int x,y,z; /* board coordinates. x=FREEEND means this one is free */
int prevdir; /* The direction we went to get here. */
/* In picking what direction to go, we are biased */
/* toward this direction. This gives paths */
/* with several straight sections -- keeps one path from */
/* knotting up one part of the board. */
int nextfree; /* next unused end, LASTEND means no more */
} endtype;
endtype ends[MAXENDS];
int firstfree;
int MaxEnds = MAXENDS;
initends()
{ int i;
for (i=0; i<MaxEnds; i++) {
ends[i].x = -1;
ends[i].y = -1;
ends[i].z = -1;
ends[i].nextfree = i - 1;
}
firstfree = MaxEnds - 1;
/* NOTE that ends[0].nextfree == -1 == LASTEND. If that define ever */
/* changes, we will have to include the statement: */
/* ends[0].nextfree = LASTEND; */
}
/* IF you want to play around with the parameters that effect the way
boards are built, you will want to try different combinations of
"tries", "length", and global MaxEnds. */
int extendend(curend,tries,length)
int curend, tries, length;
{
int x, y, level;
int nd,od, nx, ny, nl, i, moves, link;
moves = 0;
x = ends[curend].x;
y = ends[curend].y;
level = ends[curend].z;
nd = ends[curend].prevdir;
/* try up to i times to add a random square */
for (i=0; i<tries && moves < length; i++) {
do { /* try not to go out of bounds */
nd = ((RangeRand(10L) < 4) ? (int)RangeRand((LONG)DIRECTIONS) : nd);
} while ((nd == NORTH && y == 1) ||
(nd == WEST && x == 1) ||
(nd == SOUTH && y == BoardMaxY-1) ||
(nd == EAST && x == BoardMaxX-1) );
/* Now that we've got a direction, see that we don't already go that way */
if (board[x][y][level].conlev[nd] != NOWHERE)
continue;
nx = x; ny = y;
switch (nd) { /* work out the new x and y */
case NORTH : ny = y - 1; break;
case SOUTH : ny = y + 1; break;
case EAST : nx = x + 1; break;
case WEST : nx = x - 1; break;
}
od = otherDir(nd);
nl = level;
/** get some level we can go onto from here **/
if (!(link=linkable(x, y, level, nd, nx, ny, nl, od))) { /* stay on the same level if we can... */
if (level > MinLevel) { /* can't stay on this level so go down */
nl = level - 1;
link = linkable(x, y, level, nd, nx, ny, nl, od); /* if we can't go down, go up */
}
if (!link && (level < MaxLevel - 1)) {
nl = level + 1; /* can't go down or level, so try up */
link = linkable(x, y, level, nd, nx, ny, nl, od);
}
}
if (link) {
board[x ][y ][level].conlev[nd] = nl;
board[nx][ny][nl ].conlev[od] = level;
render( x, y);
render(nx,ny);
moves++;
/* sometimes fork, putting our old coordinates in a free end slot. */
if (firstfree != LASTEND) { /* make sure there IS a free end slot */
if (RangeRand(10L) < 4) {
ends[firstfree].x = x;
ends[firstfree].y = y;
ends[firstfree].z = level;
ends[firstfree].prevdir = nd;
firstfree = ends[firstfree].nextfree;
}
}
ends[curend].x = x = nx;
ends[curend].y = y = ny;
ends[curend].z = level = nl;
}
}
/* if we got this far and made no moves, we are probably on dead end and */
/* should put this ends[] on the free list. */
if (moves == 0) {
ends[curend].x = FREEEND;
ends[curend].nextfree = firstfree;
firstfree = curend;
}
return(moves);
}
int xties, xlength;
int makepath()
{ int i, endsfound, totalsquares;
initends(); /* This sets the table of path ends to all zeros */
/* ends[] is a list of the ends of paths which we can add more paths */
/* onto. Note that they don't really have to be an end; they can be */
/* in the middle of a path as well. Not all of them are in use as */
/* an end all the time. Some are in a linked list of "free" ends, */
/* available for use if we want to add an end. Free (unused) array */
/* elements are marked by a .x == FREEEND. Ends get taken out of active */
/* service and put on the free list when the path generator "extendend()" */
/* fails to extend the path from that end. When no active ends remain, */
/* maze generation terminates. */
/* p.s.: I don't particularly care for the mazes that are generated by */
/* this method, nor am I particularly fond of the method. I would like */
/* to hear of other strategies for maze generation. */
ends[firstfree].x = 1 + RangeRand((LONG)BoardMaxX - 1);
ends[firstfree].y = 1 + RangeRand((LONG)BoardMaxY - 1);
ends[firstfree].z = 0;
ends[firstfree].prevdir = RangeRand((LONG)DIRECTIONS);
firstfree = ends[firstfree].nextfree;
totalsquares = 0;
do {
endsfound = 0;
for (i=0; i< MaxEnds; i++) {
if (ends[i].x != FREEEND) {
endsfound++;
totalsquares += extendend(i,xties,xlength);
}
}
} while (endsfound);
return( totalsquares );
}
pickends()
{ int x, y, z, i;
/* Find then end of a path on the bottom level. */
do {
StartX = 1 + RangeRand( (LONG) BoardMaxX-1L );
StartY = 1 + RangeRand( (LONG) BoardMaxY-1L );
StartLevel = MinLevel;
} while (references( StartX, StartY, StartLevel) != 1);
/* The reason for doing this 4 times is to make the ends as far apart as */
/* possible. If our first choice is close to the top of the tree, it */
/* can't be very far away from every leaf node. The way this works is: */
/* 1 Find a leaf node. */
/* 2 Find the leaf farthest from the one found in step 1. */
/* 3 Find the leaf farthest from the one found in step 2. ... */
/* Eventually you should end up with two good choices for the ends. */
for (i=0; i<4; i++) {
for (x=1; x<BoardMaxX; x++)
for (y=1; y<BoardMaxY; y++)
for (z=MinLevel; z<MaxLevel; z++)
board[x][y][z].compass = NOWHERE;
EndX = StartX; EndY = StartY; EndLevel = StartLevel;
board[ EndX ][ EndY ][ EndLevel ].compass = SOLVED;
topscore = 0;
buildreturnmap(EndX, EndY, EndLevel, 0);
}
board[ StartX ][ StartY ][ StartLevel ].inpath = FIRSTPIECE;
board[ EndX ][ EndY ][ EndLevel ].inpath = LASTPIECE;
CurX = StartX;
CurY = StartY;
CurLevel = StartLevel;
NewX = CurX;
NewY = CurY;
NewLevel = CurLevel;
render( StartX, StartY);
render( EndX, EndY);
}
init1( l )
int l;
{
int i, x, y, level, dir;
static int Xsize[] = { 54, 16, 24, 34 }; /* Square widths */
static int Xsqrs[] = { 10, 34, 22, 16 }; /* No. of squares */
static int Ysize[] = { 26, 10, 14, 18 }; /* Square heights */
static int Ysqrs[] = { 6, 16, 11, 9 }; /* No. of squares */
/* Reset the entire board, including a strip of squares around */
/* the edges that we never go into. */
for(x=0; x <= BOARDMAXX; x++)
for(y=0; y <= BOARDMAXY; y++)
for(level=0; level < MAXLEVELS; level++) {
board[x][y][level].inpath = NOTTRAVERSED;
board[x][y][level].compass = NOWHERE;
for(dir=0; dir < DIRECTIONS; dir++)
board[x][y][level].conlev[dir] = NOWHERE;
}
SetAPen( rp, 0L);
SetBPen( rp, 0L);
RectFill( rp, 4L, 11L, (LONG)windowW-4, (LONG)windowH-2);
MaxLevel = l; /* l==0 is a special-case first-time super-easy 1-level */
/* maze. It generates fast so we can get to the menus. */
BoardMaxX = Xsqrs[ MaxLevel ] + 1;
SquareXsize = Xsize[ MaxLevel ];
BoardMaxY = Ysqrs[ MaxLevel ] + 1;
SquareYsize = Ysize[ MaxLevel ];
if (MaxLevel == 0)
MaxLevel = 1;
makepath();
pickends();
setHint( (int)FALSE );
}
int autosolve()
{ int newDir;
square *oldSq, *newSq;
while ( (newDir = board[CurX][CurY][CurLevel].compass) != SOLVED ) {
NewX = CurX;
NewY = CurY;
NewLevel = board[CurX][CurY][CurLevel].conlev[newDir];
switch (newDir) {
case NORTH : NewY--; break;
case SOUTH : NewY++; break;
case EAST : NewX++; break;
case WEST : NewX--; break;
}
oldSq = &board[ CurX ][ CurY ][ CurLevel];
newSq = &board[ NewX ][ NewY ][ NewLevel];
oldSq->inpath = TRAVERSED;
switch (newSq->inpath) {
case TRAVERSED :
case FIRSTPIECE : /* We must have backed up to get here, so...*/
oldSq->inpath = NOTTRAVERSED; break;
default : ;
}
/* set the inpath flag of the new square in any case */
newSq->inpath = CURPIECE;
/* Also, just in case we backed up to the starting square */
/* or from the ending square.... */
board[StartX][StartY][StartLevel].inpath = FIRSTPIECE;
board[EndX ][EndY ][EndLevel ].inpath = LASTPIECE;
/* Now draw both squares */
render(CurX,CurY);
render(NewX,NewY);
/* Now new becomes current */
CurX = NewX;
CurY = NewY;
CurLevel = NewLevel;
}
}
int mousewatch()
{ static int canmove = FALSE;
int x, y, newDir;
x = ((MouseX) - 4)/SquareXsize + 1;
y = ((MouseY) - 11)/SquareYsize + 1;
x = x * SIGN(MouseX);
y = y * SIGN(MouseY);
if (x == CurX && y == CurY) {
canmove = TRUE;
}
newDir = NOWHERE;
if (x == CurX) {
if (y == CurY + 1 && y < BoardMaxY)
newDir = SOUTH;
else if (y == CurY - 1 && y > 0)
newDir = NORTH;
}
else
if (y == CurY) {
if (x == CurX + 1 && x < BoardMaxX)
newDir = EAST;
else if (x == CurX - 1 && x > 0)
newDir = WEST;
}
if (newDir == NOWHERE ) {
canmove = FALSE;
return(NOWHERE);
}
NewLevel = board[CurX][CurY][CurLevel].conlev[newDir];
if (NewLevel == NOWHERE) {
canmove = FALSE;
return(NOWHERE);
}
NewX = x;
NewY = y;
return(newDir);
}
int trymove() /* returns TRUE if we won, FALSE if we didn't win. */
{ int newDir;
square *oldSq, *newSq;
newDir = mousewatch();
if (newDir == NOWHERE)
return(board[CurX][CurY][CurLevel].compass == SOLVED);
oldSq = &board[ CurX ][ CurY ][ CurLevel];
newSq = &board[ NewX ][ NewY ][ NewLevel];
oldSq->inpath = TRAVERSED;
switch (newSq->inpath) {
case TRAVERSED :
case FIRSTPIECE : /* We must have backed up to get here, so...*/
oldSq->inpath = NOTTRAVERSED; break;
default : ;
}
/* set the inpath flag of the new square in any case */
newSq->inpath = CURPIECE;
/* Also, just in case we backed up from the starting square */
/* or the ending square.... */
board[StartX][StartY][StartLevel].inpath = FIRSTPIECE;
board[EndX ][EndY ][EndLevel ].inpath = LASTPIECE;
/* Now draw both squares */
render(CurX,CurY);
render(NewX,NewY);
/* Now new becomes current */
CurX = NewX;
CurY = NewY;
CurLevel = NewLevel;
/* ?did we win? */
setHint((int)(newSq->compass == SOLVED));
if (newSq->compass == SOLVED) {
cycle( 3 );
return( (int)TRUE );
}
return( (int)FALSE);
}
HandleMenu(menucode)
USHORT menucode;
{
ModifyIDCMP( window, (ULONG) MIDCMP2 );
switch ( ITEMNUM( menucode) ) {
case 0 : about() ; break;
case 1 : init1( 1 ); break;
case 2 : init1( 2 ); break;
case 3 : init1( 3 ); break;
case 4 : autosolve(); break;
}
ModifyIDCMP( window, (ULONG) MIDCMP1 );
}
EventLoop()
{
struct IntuiMessage *mesg;
ULONG class, code, mousemoved, closewindow, menupick;
USHORT menucode;
closewindow = FALSE;
ModifyIDCMP( window, (ULONG) MIDCMP1 );
do {
mousemoved = FALSE;
menupick = FALSE;
Wait( (LONG) 1 << window -> UserPort -> mp_SigBit);
while ((mesg=(struct IntuiMessage *)GetMsg(window->UserPort))) {
class = mesg->Class;
code = mesg->Code;
MouseX = mesg->MouseX;
MouseY = mesg->MouseY;
ReplyMsg(mesg);
if (class == MOUSEMOVE) mousemoved = TRUE;
if (class == CLOSEWINDOW) closewindow = TRUE;
if (class == MOUSEBUTTONS && code == SELECTDOWN)
setHint( (int) TRUE);
if (class == MENUPICK) {
menupick = TRUE;
menucode = code;
}
}
if (mousemoved) trymove();
if (menupick) HandleMenu(menucode);
} while (closewindow == FALSE);
}
struct TextFont *textfont = NULL;
openstuff()
{ ULONG Seconds, Micros;
IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0L);
if (IntuitionBase == NULL) {closestuff(); exit(0);}
GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0L);
if (GfxBase == NULL) {closestuff(); exit(0); }
/* My <functions.h> shows OpenFont() returns a *Font. */
/* I think that is an error! */
textfont = (struct TextFont *)OpenFont( &font );
if (textfont==NULL) { closestuff(); exit(0); }
if (( window=(struct Window *)OpenWindow(&nw))==NULL) {
closestuff();
exit(0);
}
if (SetFont(window->RPort,textfont)==0) { closestuff(); exit(0); }
SetWindowTitles(window,(UBYTE *)" TML's AmigaMaze ver. 1.2 ",
(UBYTE *)"TML's AmigaMaze First distributed on JUMPDISK.");
SetMenuStrip( window, &menu);
/* This next bit is a cludge because I can't seem to get */
/* extern ULONG RangeSeed; */
/* to work. Anyone got any ideas? */
/* LATER: It turns out that the RangeSeed is not public */
/* in the Manx sources. Hope they fix this someday. */
CurrentTime(&Seconds,&Micros);
Micros = Micros & (ULONG) 0x0fff;
for (Seconds=0; Seconds < Micros; Seconds++)
RangeRand(4L);
}
closestuff()
{ int i;
for(i=0; i<2; i++) {
if (tmpBM.Planes[i])
FreeRaster(tmpBM.Planes[i],tmpBMxy,tmpBMxy);
}
if (window) {
ClearMenuStrip( window );
CloseWindow(window);
}
if (textfont) CloseFont(textfont);
if (GfxBase) CloseLibrary(GfxBase);
if (IntuitionBase) CloseLibrary(IntuitionBase);
}
main(/*argc,argv*/)
/* int argc; */
/* char *argv[]; */
{ UWORD areabuffer[250], areabuffer2[250];
struct TmpRas tmpras, tmprasB;
struct AreaInfo areainfo, areainfo2;
PLANEPTR plane,planeB;
int i;
/* if (argc > 1) */
/* xties = atoi(argv[1]); */
/* if (xties < 1 || xties > 100) */
xties = 30;
/* if (argc > 2) */
/* xlength = atoi(argv[2]); */
/* if (xlength < 1 || xlength > 100) */
xlength = 9;
/* if (argc > 3) */
/* MaxEnds = atoi(argv[3]); */
/* if (MaxEnds < 2 || MaxEnds > MAXENDS) */
MaxEnds = MAXENDS;
MaxLevel = 1;
MinLevel = 0;
openstuff();
InitBitMap( &tmpBM, 2L, tmpBMxy, tmpBMxy );
for (i=0; i<2; i++) {
if ((tmpBM.Planes[i] = (PLANEPTR)AllocRaster(tmpBMxy,tmpBMxy))==NULL) {
closestuff();
exit(0);
}
}
rp = window->RPort;
if ((plane = AllocRaster(windowW,windowH))==NULL) {
closestuff();
exit(0);
}
if ((planeB = AllocRaster(tmpBMxy,tmpBMxy))==NULL) {
FreeRaster(plane, windowW,windowH);
closestuff();
exit(0);
}
InitArea(&areainfo, areabuffer, 90L);
InitArea(&areainfo2,areabuffer2,90L);
InitTmpRas(&tmpras, plane, RASSIZE( windowW, windowH));
InitTmpRas(&tmprasB,planeB,RASSIZE( tmpBMxy, tmpBMxy));
rp->AreaInfo = &areainfo;
rp->TmpRas = &tmpras;
InitRastPort( tmpRP );
if (SetFont(tmpRP,textfont)==0) { closestuff(); exit(0); }
tmpRP->BitMap = &tmpBM;
tmpRP->AreaInfo = &areainfo2;
tmpRP->TmpRas = &tmprasB;
init1(0); /* Zero is a special case. Super simple fast easy maze
so the user can get to the menus w/o waiting so long. */
ModifyIDCMP( window, (ULONG) MIDCMP2 );
about();
EventLoop();
FreeRaster(plane, windowW, windowH);
FreeRaster(planeB,tmpBMxy, tmpBMxy);
closestuff();
}
/* Save some code space by stubbing _wb_parse() and _cli_parse(). */
void _wb_parse()
{ }
void _cli_parse()
{ }